<template>
	<view class="raffle-wheel" :style="{ width: canvasWidth + 61 + 'px', height: canvasHeight + 61 + 'px' }">
		<view class="raffle-wheel-wrap" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }">
			<canvas
				:class="className"
				:canvas-id="canvasId"
				:width="canvasWidth"
				:height="canvasHeight"
				:style="{
					width: canvasWidth + 'px',
					height: canvasHeight + 'px'
				}"
			/>
			<image
				class="canvas-img"
				:src="canvasImg"
				:style="{
					width: canvasWidth + 'px',
					height: canvasHeight + 'px',
					transform: `rotate(${canvasAngle + targetAngle}deg)`,
					transitionDuration: `${transitionDuration}s`
				}"
				v-if="canvasImg"
			></image>
			<view class="raffle-wheel__action" @click="handleAction"></view>
			<!-- 为了兼容 app 端 ctx.measureText 所需的标签 -->
			<text class="raffle-wheel__measureText">{{ measureText }}</text>
		</view>
	</view>
</template>

<script>
// var upng = require('../../utils/UPNG.js');
export default {
	name: 'RaffleWheel',
	props: {
		// canvas 宽度
		canvasWidth: {
			type: Number,
			default: 240
		},
		// canvas 高度
		canvasHeight: {
			type: Number,
			default: 240
		},
		// 奖品列表
		prizeList: {
			type: Array,
			// 必须是偶数
			validator: function(value) {
				return value.length % 2 === 0;
			},
			required: true
		},
		// 奖品区块对应背景颜色
		colors: {
			type: Array,
			default: () => ['#CA9DF1', '#8133D2'],
			// 必须是偶数且仅为 2 个颜色相互交替
			validator: function(value) {
				return value.length === 2;
			}
		},
		// 旋转动画时间 单位s
		duration: {
			type: Number,
			default: 8
		},
		// 旋转的圈数
		ringCount: {
			type: Number,
			default: 8
		},
		// 字体颜色
		fontColor: {
			// type: String,
			// default: '#A34533'
			type: Array,
			default: () => ['#8133D2', '#CA9DF1'],
			// 必须是偶数且仅为 2 个颜色相互交替
			validator: function(value) {
				return value.length === 2;
			}
		},
		// 文字的大小
		fontSize: {
			type: String,
			default: '20px'
		},
		// 奖品文字多行情况下的行高
		lineHeight: {
			type: Number,
			default: 20
		},
		// 奖品名称所对应的 key 值
		strKey: {
			type: String,
			required: true
		},
		// 奖品文字总长度限制
		strMaxLength: {
			type: Number,
			default: 7
		},
		// 奖品文字多行情况下第一行文字长度
		strLineLength: {
			type: Number,
			default: 4
		}
	},
	data() {
		return {
			// 画板className
			className: 'raffle-wheel__canvas',
			// 画板标识
			canvasId: 'raffleWheelCanvas',
			// 画板导出的图片
			canvasImg: '',
			// 旋转到奖品目标需要的角度
			targetAngle: 0,
			// 旋转动画时间 单位 s
			transitionDuration: 0,
			// 是否正在旋转
			isRotate: false,
			// 当前停留在那个奖品的序号
			stayIndex: 0,
			// 解决 app 不支持 measureText 的问题
			measureText: ''
		};
	},
	computed: {
		// 设备像素密度
		pixelRatio() {
			return uni.getSystemInfoSync().pixelRatio;
		},
		// 根据奖品列表计算 canvas 旋转角度
		// 让 启动按钮指针 在奖品分区中间 position = 45
		// 让 启动按钮指针 在奖品分区边界 position = 90
		canvasAngle() {
			let prizeCount = this.prizeList.length;
			let position = 90;
			if (prizeCount % 4 !== 0) {
				return 0;
			} else {
				let num = prizeCount / 4;
				return num % 2 === 0 ? position / num : position;
			}
		},
		// 根据画板的宽度计算奖品文字与中心点的距离
		textRadius() {
			return Math.round(this.canvasWidth / 3.2);
		}
	},
	methods: {
		// 开始旋转
		handleStartRotate(targetIndex) {
			// 奖品总数
			let prizeCount = this.prizeList.length;
			let baseAngle = 360 / prizeCount;
			let angles = 0;
			if (this.targetAngle === 0) {
				console.log('第一次旋转');
				// 因为第一个奖品是从0°开始的，即水平向右方向
				// 第一次旋转角度 = 270度 - (停留的序号-目标序号) * 每个奖品区间角度 - 每个奖品区间角度的一半 - canvas自身旋转的度数
				angles = 270 - (targetIndex - this.stayIndex) * baseAngle - baseAngle / 2 - this.canvasAngle;
			} else {
				console.log('后续旋转');
				// 后续继续旋转 就只需要计算停留的位置与目标位置的角度
				angles = -(targetIndex - this.stayIndex) * baseAngle;
			}
			// 更新目前序号
			this.stayIndex = targetIndex;
			// 转 8 圈，圈数越多，转的越快
			this.targetAngle += angles + 360 * this.ringCount;

			// 计算转盘结束对时间，预加一些延迟确保转盘停止后触发结束事件
			let endTime = this.transitionDuration * 1000 + 100;
			setTimeout(() => {
				console.log('isRotate2:', this.isRotate);
				this.isRotate = false;
				this.$emit('actionEnd');
			}, endTime);
		},
		// 点击 开始抽奖 按钮
		handleAction() {
			console.log('isRotate:', this.isRotate);
			if (this.isRotate) return;
			this.isRotate = true;
			this.$emit('actionStart');
		},
		// 渲染转盘
		async drawWheelCanvas() {
			// 获取 canvas 画布
			const canvasId = this.canvasId;
			const ctx = uni.createCanvasContext(canvasId, this);

			// canvas 的宽高
			let canvasW = this.canvasWidth;
			let canvasH = this.canvasHeight;

			// 根据奖品个数计算 角度
			let prizeCount = this.prizeList.length;
			let baseAngle = (Math.PI * 2) / prizeCount;

			// 设置描边颜色
			// ctx.strokeStyle = '#CE9660';
			ctx.strokeStyle = '#FDD14C';

			// 设置字号字体，Canvas 文本字号字体的默认值是 10px sans-serif，这里必须对 字号 字体 同时覆盖
			let family = "-apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', STHeiti, 'Microsoft Yahei', Tahoma, Simsun, sans-serif";
			ctx.font = `${this.fontSize} ${family}`;

			// 注意，开始画的位置是从0°角的位置开始画的。也就是水平向右的方向。
			// 画具体内容
			for (let i = 0; i < prizeCount; i++) {
				// 当前角度
				let angle = i * baseAngle;

				// 保存当前画布的状态
				ctx.save();

				// 开始画内容
				ctx.beginPath();

				// 开始画圆弧
				// x => 圆弧对应的圆心横坐标 x
				// y => 圆弧对应的圆心横坐标 y
				// radius => 圆弧的半径大小
				// startAngle => 圆弧开始的角度，单位是弧度
				// endAngle => 圆弧结束的角度，单位是弧度
				// anticlockwise(可选) => 绘制方向，true 为逆时针，false 为顺时针
				let outsideRadius = canvasW / 2;
				let insideRadius = 20;
				ctx.arc(canvasW * 0.5, canvasH * 0.5, outsideRadius, angle, angle + baseAngle, false);
				ctx.arc(canvasW * 0.5, canvasH * 0.5, insideRadius, angle + baseAngle, angle, true);

				// 开始链接线条
				ctx.stroke();
				// 每个奖品区块背景填充颜色
				ctx.fillStyle = this.colors[i % 2];
				// 填充颜色
				ctx.fill();

				// 开始绘制奖品内容
				ctx.fillStyle = this.fontColor[i % 2];
				let rewardName = this.strLimit(this.prizeList[i][this.strKey]);

				// translate方法重新映射画布上的 (0,0) 位置
				let translateX = canvasW * 0.5 + Math.cos(angle + baseAngle / 2) * this.textRadius;
				let translateY = canvasH * 0.5 + Math.sin(angle + baseAngle / 2) * this.textRadius;
				ctx.translate(translateX, translateY);

				// rotate方法旋转当前的绘图，因为文字是和当前扇形中心线垂直的
				ctx.rotate(angle + baseAngle / 2 + Math.PI / 2);

				// 设置文本位置并处理换行

				// 是否需要换行
				let isLineBreak = rewardName.length > this.strLineLength;
				if (isLineBreak) {
					// 获得多行文本数组
					rewardName = rewardName.substring(0, this.strLineLength) + ',' + rewardName.substring(this.strLineLength);
					let rewardNames = rewardName.split(',');

					// 循环文本数组，计算每一行的文本宽度
					for (let j = 0; j < rewardNames.length; j++) {
						if (ctx.measureText && ctx.measureText(rewardNames[j]).width) {
							// 文本的宽度信息
							let tempStrSize = ctx.measureText(rewardNames[j]);
							ctx.fillText(rewardNames[j], -tempStrSize.width / 2, j * this.lineHeight);
						} else {
							this.measureText = rewardNames[j];

							// 等待页面重新渲染
							await this.$nextTick();

							let textWidth = await this.getTextWidth();

							ctx.fillText(rewardNames[j], -textWidth / 2, j * this.lineHeight);
							console.log(rewardNames[j], textWidth, i);
						}
					}
				} else {
					if (ctx.measureText && ctx.measureText(rewardName).width) {
						// 文本的宽度信息
						let tempStrSize = ctx.measureText(rewardName);
						ctx.fillText(rewardName, -tempStrSize.width / 2, 0);
					} else {
						this.measureText = rewardName;

						// 等待页面重新渲染
						await this.$nextTick();

						let textWidth = await this.getTextWidth();

						ctx.fillText(rewardName, -textWidth / 2, 0);
					}
				}

				ctx.restore();
			}

			// 保存绘图并导出图片
			ctx.draw(true, () => {
				let drawTimer = setTimeout(() => {
					clearTimeout(drawTimer);
					drawTimer = null;

					// wx.canvasGetImageData({
					// 	canvasId: this.canvasId,
					// 	x: 0,
					// 	y: 0,
					// 	width: this.canvasWidth * this.pixelRatio,
					// 	height: this.canvasHeight * this.pixelRatio,
					// 	success: (res)=> {
					// 		// console.log(res)
					// 		let pngData = upng.encode([res.data.buffer], res.width, res.height);
					// 		// let bs64 = wx.arrayBufferToBase64(pngData);
					// 		// this.canvasImg = 'data:image/jgp;base64,' + bs64;
					// 		this.canvasImg = pngData;
					// 		console.log("-------",pngData,this.canvasImg)
					// 		this.$emit('done')
					// 	},
					// 	fail: (err)=> {
					// 		console.log("err",err);
					// 	}
					// },this)

					uni.canvasToTempFilePath(
						{
							destWidth: this.canvasWidth * this.pixelRatio,
							destHeight: this.canvasHeight * this.pixelRatio,
							canvasId: this.canvasId,
							success: res => {
								// 在 H5 平台下，tempFilePath 为 base64
								// console.log("图片", res.tempFilePath)
								// uni.showModal({
								// 	title: '提示',
								// 	content: res.tempFilePath,
								// 	success: function(res) {
								// 		if (res.confirm) {
								// 			console.log('用户点击确定');
								// 		} else if (res.cancel) {
								// 			console.log('用户点击取消');
								// 		}
								// 	}
								// });
								// wx.downloadFile({
								// 	url: res.tempFilePath, //仅为示例，并非真实的资源
								// 	success:(resx)=> {
								// 		console.log('down res', resx);
								// 		this.canvasImg = resx.tempFilePath;
								// 	}
								// });
								// let bs64 = wx.arrayBufferToBase64(res.tempFilePath);
								// this.canvasImg = res.tempFilePath;
								this.canvasImg = res.tempFilePath;
								// 通知父级组件，抽奖转品生成图片完成
								this.$emit('done');
								uni.hideLoading();
							}
						},
						this
					);
				}, 20);
			});
		},
		// 兼容 app 端不支持 ctx.measureText
		// 已知问题：初始绘制时，低端安卓机 平均耗时 2s
		getTextWidth() {
			return new Promise((resolve, reject) => {
				uni.createSelectorQuery()
					.in(this)
					.select('.raffle-wheel__measureText')
					.fields(
						{
							size: true
						},
						res => {
							resolve(res.width);
						}
					)
					.exec();
			});
		},
		// 处理文字溢出
		strLimit(value) {
			let maxLength = this.strMaxLength;
			if (!value || !maxLength) return value;
			return value.length > maxLength ? value.slice(0, maxLength - 1) + '...' : value;
		}
	},
	mounted() {
		uni.showLoading({
			title: '加载中...',
			mask: true
		});
		this.$nextTick(() => {
			let stoTimer = setTimeout(() => {
				clearTimeout(stoTimer);
				stoTimer = null;

				this.drawWheelCanvas();
				this.transitionDuration = this.duration;
			}, 50);
		});
	}
};
</script>

<style lang="scss" scoped>
$actionBgUrl: '~static/raffle-wheel/raffle-wheel__action';
$raffleBgUrl: '~static/raffle-wheel/raffle-wheel__bg';

.raffle-wheel {
	position: relative;
	left: 0;
	top: 0;
	display: flex;
	justify-content: center;
	align-items: center;
}

.raffle-wheel {
	display: flex;
	justify-content: center;
	align-items: center;
	margin: 0 auto;
	background-repeat: no-repeat;
	background-position: center center;
	background-size: contain;
	background-image: url($raffleBgUrl+'.png');
	@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {
		background-image: url($raffleBgUrl+'2x.png');
	}
	@media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) {
		background-image: url($raffleBgUrl+'3x.png');
	}
}

.raffle-wheel__canvas {
	position: absolute;
	left: -9999px;
	opacity: 0;
	display: flex;
	justify-content: center;
	align-items: center;
}

.raffle-wheel__action {
	position: absolute;
	top: calc(50% - 58px);
	left: calc(50% - 58px);
	width: 114px;
	height: 114px;
	background-repeat: no-repeat;
	background-position: center center;
	background-size: contain;
	background-image: url($actionBgUrl+'.png');

	@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {
		background-image: url($actionBgUrl+'2x.png');
	}

	@media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) {
		background-image: url($actionBgUrl+'3x.png');
	}
}

.raffle-wheel__measureText {
	position: absolute;
	left: 0;
	top: 0;
	white-space: nowrap;
	font-size: 12px;
	opacity: 0;
}

.canvas-img {
	z-index: 0;
	transition: transform cubic-bezier(0.34, 0.12, 0.05, 0.95);
}
</style>
